/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.config;

import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.serialize.Serialization;
import org.apache.dubbo.common.status.StatusChecker;
import org.apache.dubbo.common.threadpool.ThreadPool;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.remoting.Codec;
import org.apache.dubbo.remoting.Dispatcher;
import org.apache.dubbo.remoting.Transporter;
import org.apache.dubbo.remoting.exchange.Exchanger;
import org.apache.dubbo.remoting.telnet.TelnetHandler;
import org.apache.dubbo.rpc.Protocol;

import java.util.Map;

/**
 * ProtocolConfig
 *
 * @export
 */
public class ProtocolConfig extends AbstractConfig {

  private static final long serialVersionUID = 6913423882496634749L;

  /**
   * Protocol name
   */
  private String name;

  /**
   * Service ip address (when there are multiple network cards available)
   */
  private String host;

  /**
   * Service port
   */
  private Integer port;

  /**
   * Context path
   */
  private String contextpath;

  /**
   * Thread pool
   */
  private String threadpool;

  /**
   * Thread pool core thread size
   */
  private Integer corethreads;

  /**
   * Thread pool size (fixed size)
   */
  private Integer threads;

  /**
   * IO thread pool size (fixed size)
   */
  private Integer iothreads;

  /**
   * Thread pool's queue length
   */
  private Integer queues;

  /**
   * Max acceptable connections
   */
  private Integer accepts;

  /**
   * Protocol codec
   */
  private String codec;

  /**
   * Serialization
   */
  private String serialization;

  /**
   * Charset
   */
  private String charset;

  /**
   * Payload max length
   */
  private Integer payload;

  /**
   * Buffer size
   */
  private Integer buffer;

  /**
   * Heartbeat interval
   */
  private Integer heartbeat;

  /**
   * Access log
   */
  private String accesslog;

  /**
   * Transfort
   */
  private String transporter;

  /**
   * How information is exchanged
   */
  private String exchanger;

  /**
   * Thread dispatch mode
   */
  private String dispatcher;

  /**
   * Networker
   */
  private String networker;

  /**
   * Sever impl
   */
  private String server;

  /**
   * Client impl
   */
  private String client;

  /**
   * Supported telnet commands, separated with comma.
   */
  private String telnet;

  /**
   * Command line prompt
   */
  private String prompt;

  /**
   * Status check
   */
  private String status;

  /**
   * Whether to register
   */
  private Boolean register;

  /**
   * whether it is a persistent connection
   */
  //TODO add this to provider config
  private Boolean keepAlive;

  // TODO add this to provider config
  private String optimizer;

  /**
   * The extension
   */
  private String extension;

  /**
   * The customized parameters
   */
  private Map<String, String> parameters;

  /**
   * If it's default
   */
  private Boolean isDefault;

  public ProtocolConfig() {
  }

  public ProtocolConfig(String name) {
    setName(name);
  }

  public ProtocolConfig(String name, int port) {
    setName(name);
    setPort(port);
  }

  @Parameter(excluded = true)
  public String getName() {
    return name;
  }

  public final void setName(String name) {
    checkName("name", name);
    this.name = name;
    this.updateIdIfAbsent(name);
  }

  @Parameter(excluded = true)
  public String getHost() {
    return host;
  }

  public void setHost(String host) {
    checkName(Constants.HOST_KEY, host);
    this.host = host;
  }

  @Parameter(excluded = true)
  public Integer getPort() {
    return port;
  }

  public final void setPort(Integer port) {
    this.port = port;
  }

  @Deprecated
  @Parameter(excluded = true)
  public String getPath() {
    return getContextpath();
  }

  @Deprecated
  public void setPath(String path) {
    setContextpath(path);
  }

  @Parameter(excluded = true)
  public String getContextpath() {
    return contextpath;
  }

  public void setContextpath(String contextpath) {
    checkPathName("contextpath", contextpath);
    this.contextpath = contextpath;
  }

  public String getThreadpool() {
    return threadpool;
  }

  public void setThreadpool(String threadpool) {
    checkExtension(ThreadPool.class, Constants.THREADPOOL_KEY, threadpool);
    this.threadpool = threadpool;
  }

  public Integer getCorethreads() {
    return corethreads;
  }

  public void setCorethreads(Integer corethreads) {
    this.corethreads = corethreads;
  }

  public Integer getThreads() {
    return threads;
  }

  public void setThreads(Integer threads) {
    this.threads = threads;
  }

  public Integer getIothreads() {
    return iothreads;
  }

  public void setIothreads(Integer iothreads) {
    this.iothreads = iothreads;
  }

  public Integer getQueues() {
    return queues;
  }

  public void setQueues(Integer queues) {
    this.queues = queues;
  }

  public Integer getAccepts() {
    return accepts;
  }

  public void setAccepts(Integer accepts) {
    this.accepts = accepts;
  }

  public String getCodec() {
    return codec;
  }

  public void setCodec(String codec) {
    if (Constants.DUBBO_PROTOCOL.equals(name)) {
      checkMultiExtension(Codec.class, Constants.CODEC_KEY, codec);
    }
    this.codec = codec;
  }

  public String getSerialization() {
    return serialization;
  }

  public void setSerialization(String serialization) {
    if (Constants.DUBBO_PROTOCOL.equals(name)) {
      checkMultiExtension(Serialization.class, Constants.SERIALIZATION_KEY, serialization);
    }
    this.serialization = serialization;
  }

  public String getCharset() {
    return charset;
  }

  public void setCharset(String charset) {
    this.charset = charset;
  }

  public Integer getPayload() {
    return payload;
  }

  public void setPayload(Integer payload) {
    this.payload = payload;
  }

  public Integer getBuffer() {
    return buffer;
  }

  public void setBuffer(Integer buffer) {
    this.buffer = buffer;
  }

  public Integer getHeartbeat() {
    return heartbeat;
  }

  public void setHeartbeat(Integer heartbeat) {
    this.heartbeat = heartbeat;
  }

  public String getServer() {
    return server;
  }

  public void setServer(String server) {
    if (Constants.DUBBO_PROTOCOL.equals(name)) {
      checkMultiExtension(Transporter.class, Constants.SERVER_KEY, server);
    }
    this.server = server;
  }

  public String getClient() {
    return client;
  }

  public void setClient(String client) {
    if (Constants.DUBBO_PROTOCOL.equals(name)) {
      checkMultiExtension(Transporter.class, Constants.CLIENT_KEY, client);
    }
    this.client = client;
  }

  public String getAccesslog() {
    return accesslog;
  }

  public void setAccesslog(String accesslog) {
    this.accesslog = accesslog;
  }

  public String getTelnet() {
    return telnet;
  }

  public void setTelnet(String telnet) {
    checkMultiExtension(TelnetHandler.class, Constants.TELNET, telnet);
    this.telnet = telnet;
  }

  @Parameter(escaped = true)
  public String getPrompt() {
    return prompt;
  }

  public void setPrompt(String prompt) {
    this.prompt = prompt;
  }

  public String getStatus() {
    return status;
  }

  public void setStatus(String status) {
    checkMultiExtension(StatusChecker.class, "status", status);
    this.status = status;
  }

  public Boolean isRegister() {
    return register;
  }

  public void setRegister(Boolean register) {
    this.register = register;
  }

  public String getTransporter() {
    return transporter;
  }

  public void setTransporter(String transporter) {
    checkExtension(Transporter.class, Constants.TRANSPORTER_KEY, transporter);
    this.transporter = transporter;
  }

  public String getExchanger() {
    return exchanger;
  }

  public void setExchanger(String exchanger) {
    checkExtension(Exchanger.class, Constants.EXCHANGER_KEY, exchanger);
    this.exchanger = exchanger;
  }

  /**
   * typo, switch to use {@link #getDispatcher()}
   *
   * @deprecated {@link #getDispatcher()}
   */
  @Deprecated
  @Parameter(excluded = true)
  public String getDispather() {
    return getDispatcher();
  }

  /**
   * typo, switch to use {@link #getDispatcher()}
   *
   * @deprecated {@link #setDispatcher(String)}
   */
  @Deprecated
  public void setDispather(String dispather) {
    setDispatcher(dispather);
  }

  public String getDispatcher() {
    return dispatcher;
  }

  public void setDispatcher(String dispatcher) {
    checkExtension(Dispatcher.class, Constants.DISPACTHER_KEY, dispatcher);
    this.dispatcher = dispatcher;
  }

  public String getNetworker() {
    return networker;
  }

  public void setNetworker(String networker) {
    this.networker = networker;
  }

  public Map<String, String> getParameters() {
    return parameters;
  }

  public void setParameters(Map<String, String> parameters) {
    this.parameters = parameters;
  }

  public Boolean isDefault() {
    return isDefault;
  }

  public void setDefault(Boolean isDefault) {
    this.isDefault = isDefault;
  }

  public Boolean getKeepAlive() {
    return keepAlive;
  }

  public void setKeepAlive(Boolean keepAlive) {
    this.keepAlive = keepAlive;
  }

  public String getOptimizer() {
    return optimizer;
  }

  public void setOptimizer(String optimizer) {
    this.optimizer = optimizer;
  }

  public String getExtension() {
    return extension;
  }

  public void setExtension(String extension) {
    this.extension = extension;
  }

  public void destroy() {
    if (name != null) {
      ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy();
    }
  }

  @Override
  public void refresh() {
    if (StringUtils.isEmpty(this.getName())) {
      this.setName(Constants.DUBBO_VERSION_KEY);
    }
    super.refresh();
    if (StringUtils.isNotEmpty(this.getId())) {
      this.setPrefix(Constants.PROTOCOLS_SUFFIX);
      super.refresh();
    }
  }

  @Override
  @Parameter(excluded = true)
  public boolean isValid() {
    return StringUtils.isNotEmpty(name);
  }
}
